/*
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.ourstreets.fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.transition.ChangeBounds;
import android.transition.Fade;
import android.transition.Transition;
import android.transition.TransitionInflater;
import android.transition.TransitionManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.maps.android.clustering.ClusterManager;
import com.google.samples.apps.ourstreets.R;
import com.google.samples.apps.ourstreets.data.DataView;
import com.google.samples.apps.ourstreets.data.DetailPresenter;
import com.google.samples.apps.ourstreets.data.IntentKeys;
import com.google.samples.apps.ourstreets.map.DetailClusterManager;
import com.google.samples.apps.ourstreets.map.OnCameraPositionUpdateListener;
import com.google.samples.apps.ourstreets.model.Detail;
import com.google.samples.apps.ourstreets.model.Gallery;
import com.google.samples.apps.ourstreets.view.ViewUtils;
import java.util.List;
/**
* A {@link Fragment} that displays {@link Detail}s.
* and a {@link com.google.android.gms.maps.MapView}.
*/
public class DetailFragment extends Fragment implements DataView<Detail> {
public static final String TAG = "DetailFragment";
private MapView mMapView;
private FloatingActionButton mFloatingActionButton;
private Gallery mGallery;
private CameraPosition mCameraPosition;
private int mMapPadding;
private OnCameraPositionUpdateListener mCameraListener;
private ClusterManager.OnClusterItemClickListener<Detail> mOnClusterItemClickListener;
private GoogleMap.OnMapClickListener mOnMapClickListener;
private Detail mSelectedDetail;
private Transition mDescriptionChange;
/**
* Create a new instance with details for a given {@link Gallery}.
*
* @param context The context this runs in.
* @param gallery The gallery of which the details should be displayed.
* @return A newly instantiated fragment.
*/
public static DetailFragment newInstance(@NonNull Context context, @NonNull Gallery gallery,
@NonNull CameraPosition cameraPosition) {
DetailFragment fragment = new DetailFragment();
Bundle args = new Bundle();
args.putParcelable(IntentKeys.GALLERY, gallery);
args.putParcelable(IntentKeys.CAMERA_POSITION, cameraPosition);
fragment.setArguments(args);
final TransitionInflater inflater = TransitionInflater.from(context);
fragment.setSharedElementEnterTransition(
inflater.inflateTransition(R.transition.detail_shared_enter));
fragment.setEnterTransition(new Fade());
return fragment;
}
public DetailFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMapPadding = getResources().getDimensionPixelSize(R.dimen.padding_map);
if (savedInstanceState != null) {
mCameraPosition = savedInstanceState.getParcelable(IntentKeys.CAMERA_POSITION);
mSelectedDetail = savedInstanceState.getParcelable(IntentKeys.DETAIL);
}
Bundle arguments;
if ((arguments = getArguments()) != null) {
mGallery = arguments.getParcelable(IntentKeys.GALLERY);
if (mCameraPosition == null) {
mCameraPosition = arguments.getParcelable(IntentKeys.CAMERA_POSITION);
}
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewUtils.setStatusBarColor(getActivity(), R.color.status_bar_color);
View contentView = inflater.inflate(R.layout.fragment_detail, container, false);
mFloatingActionButton = (FloatingActionButton) contentView.findViewById(R.id.fab);
mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mSelectedDetail == null) {
return;
}
showStreetViewFragment(mSelectedDetail);
}
});
if (mMapView == null) {
mMapView = (MapView) contentView.findViewById(R.id.map_view);
mMapView.onCreate(null);
}
return contentView;
}
@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
mDescriptionChange = new ChangeBounds();
mDescriptionChange.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
}
@Override
public void onTransitionEnd(Transition transition) {
applyMapPadding();
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
});
if (ViewUtils.isMainDisplayInLandscape(getContext())) {
ViewUtils.applyTopWindowInsetsForView(view.findViewById(R.id.description_layout));
}
loadDetails();
if (mSelectedDetail == null) {
setDescriptionText(mGallery);
} else {
setDescriptionText(mSelectedDetail);
}
applyMapPadding();
}
private void applyMapPadding() {
if (mMapView == null) {
return;
}
mMapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(GoogleMap googleMap) {
View descriptionLayout = getView().findViewById(R.id.description_layout);
googleMap.setPadding(0, 0, 0, descriptionLayout.getMeasuredHeight());
}
});
}
@Override
public void onDestroyView() {
mFloatingActionButton = null;
super.onDestroyView();
}
@Override
public void onResume() {
mMapView.onResume();
super.onResume();
}
@Override
public void onSaveInstanceState(final Bundle outState) {
mMapView.onSaveInstanceState(outState);
outState.putParcelable(IntentKeys.CAMERA_POSITION, mCameraPosition);
outState.putParcelable(IntentKeys.DETAIL, mSelectedDetail);
super.onSaveInstanceState(outState);
}
@Override
public void onPause() {
mMapView.onPause();
super.onPause();
}
@Override
public void onDestroy() {
mMapView.onDestroy();
super.onDestroy();
}
@Override
public void onLowMemory() {
mMapView.onLowMemory();
super.onLowMemory();
}
@Override
public void showData(List<Detail> data) {
mGallery.replaceDetails(data);
initMapAsync();
}
@Override
public void showError() {
// TODO: implement failed view
Log.i(TAG, "showError: Loading failed");
}
/**
* Enable updating of detail information.
*
* @param gallery The new gallery to display.
* @param cameraPosition The initial camera position for the details.
*/
public void setNewDetailInformation(@NonNull Gallery gallery,
@NonNull CameraPosition cameraPosition) {
mGallery = gallery;
mCameraPosition = cameraPosition;
loadDetails();
}
private void initMapAsync() {
mMapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(GoogleMap googleMap) {
initializeMap(googleMap);
}
});
}
private void initializeMap(final GoogleMap googleMap) {
googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(mCameraPosition));
googleMap.setOnMapLoadedCallback(getOnMapLoadedCallback(googleMap));
setMarkers(googleMap);
}
private void setDescriptionText(@NonNull Gallery gallery) {
setDescriptionText(gallery.getTitle(), gallery.getDescription());
mFloatingActionButton.hide();
}
private void setDescriptionText(@NonNull Detail detail) {
mSelectedDetail = detail;
setDescriptionText(detail.getTitle(), detail.getDescription());
mFloatingActionButton.show();
}
private void setDescriptionText(@NonNull String title, @NonNull String detail) {
View view = getView();
if (view == null) {
return;
}
TransitionManager.beginDelayedTransition(((ViewGroup) view), mDescriptionChange);
ViewUtils.setTextOn(view, R.id.text_title, title);
ViewUtils.setTextOn(view, R.id.text_detail, detail);
}
private void setMarkers(GoogleMap googleMap) {
final List<Detail> locations = mGallery.getDetails();
ClusterManager<Detail> clusterManager = getClusterManager(googleMap);
clusterManager.addItems(locations);
googleMap.setOnCameraChangeListener(clusterManager);
}
@NonNull
private DetailClusterManager getClusterManager(GoogleMap googleMap) {
return new DetailClusterManager(getContext(), googleMap,
getOnClusterItemClickListener(),
getOnMapClickListener(),
getOnCameraPositionUpdateListener());
}
@NonNull
private GoogleMap.OnMapClickListener getOnMapClickListener() {
if (mOnMapClickListener == null) {
mOnMapClickListener = new GoogleMap.OnMapClickListener() {
@Override
public void onMapClick(LatLng latLng) {
setDescriptionText(mGallery);
}
};
}
return mOnMapClickListener;
}
@NonNull
private ClusterManager.OnClusterItemClickListener<Detail> getOnClusterItemClickListener() {
if (mOnClusterItemClickListener == null) {
mOnClusterItemClickListener = new ClusterManager.OnClusterItemClickListener<Detail>() {
@Override
public boolean onClusterItemClick(Detail detail) {
setDescriptionText(detail);
return false;
}
};
}
return mOnClusterItemClickListener;
}
private OnCameraPositionUpdateListener getOnCameraPositionUpdateListener() {
if (mCameraListener == null) {
mCameraListener = new OnCameraPositionUpdateListener() {
@Override
public void onCameraPositionUpdate(CameraPosition cameraPosition) {
mCameraPosition = cameraPosition;
}
};
}
return mCameraListener;
}
private void showStreetViewFragment(Detail detail) {
StreetViewFragment streetViewFragment = getStreetViewFragment(detail);
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
/*
The StreetViewFragment has to be added (not replaced)
in order to get the transition animation right.
*/
transaction.add(android.R.id.content, streetViewFragment, StreetViewFragment.TAG)
.addToBackStack(StreetViewFragment.TAG);
transaction.commit();
}
@NonNull
private GoogleMap.OnMapLoadedCallback getOnMapLoadedCallback(final GoogleMap googleMap) {
return new GoogleMap.OnMapLoadedCallback() {
@Override
public void onMapLoaded() {
googleMap.getUiSettings().setMapToolbarEnabled(false);
if (mGallery.hasDetails()) {
googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
googleMap.animateCamera(
CameraUpdateFactory.newLatLngBounds(mGallery.getBounds(), mMapPadding));
}
}
};
}
@NonNull
private StreetViewFragment getStreetViewFragment(Detail detail) {
FragmentManager supportFragmentManager = getActivity().getSupportFragmentManager();
StreetViewFragment streetViewFragment = (StreetViewFragment) supportFragmentManager
.findFragmentByTag(StreetViewFragment.TAG);
if (streetViewFragment == null) {
streetViewFragment = StreetViewFragment.newInstance(detail,
ViewUtils.getCenterForView(mFloatingActionButton),
mFloatingActionButton.getWidth());
} else {
streetViewFragment.setDetail(detail);
}
return streetViewFragment;
}
private void loadDetails() {
if (mGallery == null) {
Log.w(TAG, "loadDetails: can't load data for null gallery");
showError();
return;
} else if (mGallery.hasDetails()) {
initMapAsync();
return;
}
new DetailPresenter(this, mGallery.getGalleryId()).getData();
}
}